#pragma once
#include "Core/Libs.h"
#include "Core/Libs.h"
#include "Core/Math/Math.h"
#include "Core/Macros.h"
#include <d3d12.h>
#include <vector>
#include "tiny_gltf.h"

// using namespace DirectX;
// using Microsoft::WRL::ComPtr;
namespace SL
{

// struct Vertex
// {
//     float3 position;
//     float3 normal;
//     float2 texCoord;
//     float4 tangent;
// };

struct MeshData
{
    ID3D12Resource* vertexBuffer;
    ID3D12Resource* indexBuffer;
    D3D12_VERTEX_BUFFER_VIEW vertexBufferView;
    D3D12_INDEX_BUFFER_VIEW indexBufferView;
    uint32_t indexCount;
    uint32_t materialIndex;
};

struct TextureData
{
    ID3D12Resource* resource;
    uint32_t srvIndex;
};

class GLTFLoader
{
private:
    ID3D12Device* m_device;
    ID3D12GraphicsCommandList* m_commandList;
    std::vector<ID3D12Resource*> uploadBuffers;
    std::vector<MeshData> m_meshes;
    std::vector<TextureData> m_textures;

public:
    GLTFLoader(ID3D12Device* device, ID3D12GraphicsCommandList* commandList) : m_device(device), m_commandList(commandList) {}

    bool LoadGLTF(const std::string& filename)
    {
        tinygltf::Model model;
        tinygltf::TinyGLTF loader;
        std::string error, warning;

        if (!loader.LoadASCIIFromFile(&model, &error, &warning, filename))
        {
            return false;
        }

        ProcessTextures(model);
        ProcessMeshes(model);

        return true;
    }

private:
    void ProcessTextures(const tinygltf::Model& model);
    void ProcessMeshes(const tinygltf::Model& model);

    // void CreateTextureResource(const tinygltf::Image& image, TextureData& textureData)
    // {
    //     // Determine format and bytes per pixel
    //     DXGI_FORMAT format = (image.component == 4) ? DXGI_FORMAT_R8G8B8A8_UNORM : DXGI_FORMAT_R8G8B8A8_UNORM;
    //     uint32_t bytesPerPixel = 4;

    //     // Create GPU texture resource
    //     auto textureDesc = CD3DX12_RESOURCE_DESC::Tex2D(format, image.width, image.height, 1, 1);
    //     auto heapProps = CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT);

    //     m_device->CreateCommittedResource(
    //         &heapProps, D3D12_HEAP_FLAG_NONE, &textureDesc, D3D12_RESOURCE_STATE_COPY_DEST, nullptr, IID_PPV_ARGS(&textureData.resource)
    //     );

    //     // Create upload buffer
    //     uint64_t uploadBufferSize;
    //     D3D12_PLACED_SUBRESOURCE_FOOTPRINT footprint;
    //     m_device->GetCopyableFootprints(&textureDesc, 0, 1, 0, &footprint, nullptr, nullptr, &uploadBufferSize);

    //     auto uploadDesc = CD3DX12_RESOURCE_DESC::Buffer(uploadBufferSize);
    //     heapProps = CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_UPLOAD);

    //     ComPtr<ID3D12Resource> uploadBuffer;
    //     m_device->CreateCommittedResource(
    //         &heapProps, D3D12_HEAP_FLAG_NONE, &uploadDesc, D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, IID_PPV_ARGS(&uploadBuffer)
    //     );

    //     // Map and copy image data
    //     void* mappedData;
    //     uploadBuffer->Map(0, nullptr, &mappedData);

    //     uint8_t* dst = static_cast<uint8_t*>(mappedData);
    //     const uint8_t* src = image.image.data();

    //     if (image.component == 3)
    //     {
    //         // Convert RGB to RGBA
    //         for (int y = 0; y < image.height; ++y)
    //         {
    //             for (int x = 0; x < image.width; ++x)
    //             {
    //                 int srcIdx = (y * image.width + x) * 3;
    //                 int dstIdx = y * footprint.Footprint.RowPitch + x * 4;
    //                 dst[dstIdx + 0] = src[srcIdx + 0];
    //                 dst[dstIdx + 1] = src[srcIdx + 1];
    //                 dst[dstIdx + 2] = src[srcIdx + 2];
    //                 dst[dstIdx + 3] = 255;
    //             }
    //         }
    //     }
    //     else
    //     {
    //         // Direct copy for RGBA
    //         for (int y = 0; y < image.height; ++y)
    //         {
    //             memcpy(dst + y * footprint.Footprint.RowPitch, src + y * image.width * bytesPerPixel, image.width * bytesPerPixel);
    //         }
    //     }

    //     uploadBuffer->Unmap(0, nullptr);

    //     // Schedule GPU copy
    //     D3D12_TEXTURE_COPY_LOCATION srcLocation = {};
    //     srcLocation.pResource = uploadBuffer.Get();
    //     srcLocation.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT;
    //     srcLocation.PlacedFootprint = footprint;

    //     D3D12_TEXTURE_COPY_LOCATION dstLocation = {};
    //     dstLocation.pResource = textureData.resource.Get();
    //     dstLocation.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
    //     dstLocation.SubresourceIndex = 0;

    //     m_commandList->CopyTextureRegion(&dstLocation, 0, 0, 0, &srcLocation, nullptr);

    //     // Transition to shader resource
    //     auto barrier = CD3DX12_RESOURCE_BARRIER::Transition(
    //         textureData.resource.Get(), D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE
    //     );
    //     m_commandList->ResourceBarrier(1, &barrier);

    //     // Keep upload buffer alive
    //     m_uploadBuffers.push_back(uploadBuffer);
    // }

    // void ProcessMeshes(const tinygltf::Model& model)
    // {
    //     for (const auto& mesh : model.meshes)
    //     {
    //         for (const auto& primitive : mesh.primitives)
    //         {
    //             ProcessPrimitive(model, primitive);
    //         }
    //     }
    // }

    // void ProcessPrimitive(const tinygltf::Model& model, const tinygltf::Primitive& primitive)
    // {
    //     std::vector<Vertex> vertices;
    //     std::vector<uint32_t> indices;

    //     // Extract vertex data
    //     ExtractVertices(model, primitive, vertices);
    //     ExtractIndices(model, primitive, indices);

    //     MeshData meshData = {};
    //     CreateVertexBuffer(vertices, meshData);
    //     CreateIndexBuffer(indices, meshData);

    //     meshData.indexCount = static_cast<uint32_t>(indices.size());
    //     meshData.materialIndex = primitive.material;

    //     m_meshes.push_back(std::move(meshData));
    // }

    // void ExtractVertices(const tinygltf::Model& model, const tinygltf::Primitive& primitive, std::vector<Vertex>& vertices)
    // {
    //     const float* positions = nullptr;
    //     const float* normals = nullptr;
    //     const float* texCoords = nullptr;
    //     const float* tangents = nullptr;
    //     size_t vertexCount = 0;

    //     // Get position data
    //     if (primitive.attributes.find("POSITION") != primitive.attributes.end())
    //     {
    //         const auto& accessor = model.accessors[primitive.attributes.at("POSITION")];
    //         const auto& bufferView = model.bufferViews[accessor.bufferView];
    //         const auto& buffer = model.buffers[bufferView.buffer];

    //         positions = reinterpret_cast<const float*>(&buffer.data[bufferView.byteOffset + accessor.byteOffset]);
    //         vertexCount = accessor.count;
    //     }

    //     // Get normal data
    //     if (primitive.attributes.find("NORMAL") != primitive.attributes.end())
    //     {
    //         const auto& accessor = model.accessors[primitive.attributes.at("NORMAL")];
    //         const auto& bufferView = model.bufferViews[accessor.bufferView];
    //         const auto& buffer = model.buffers[bufferView.buffer];

    //         normals = reinterpret_cast<const float*>(&buffer.data[bufferView.byteOffset + accessor.byteOffset]);
    //     }

    //     // Get texture coordinate data
    //     if (primitive.attributes.find("TEXCOORD_0") != primitive.attributes.end())
    //     {
    //         const auto& accessor = model.accessors[primitive.attributes.at("TEXCOORD_0")];
    //         const auto& bufferView = model.bufferViews[accessor.bufferView];
    //         const auto& buffer = model.buffers[bufferView.buffer];

    //         texCoords = reinterpret_cast<const float*>(&buffer.data[bufferView.byteOffset + accessor.byteOffset]);
    //     }

    //     // Get tangent data
    //     if (primitive.attributes.find("TANGENT") != primitive.attributes.end())
    //     {
    //         const auto& accessor = model.accessors[primitive.attributes.at("TANGENT")];
    //         const auto& bufferView = model.bufferViews[accessor.bufferView];
    //         const auto& buffer = model.buffers[bufferView.buffer];

    //         tangents = reinterpret_cast<const float*>(&buffer.data[bufferView.byteOffset + accessor.byteOffset]);
    //     }

    //     // Build vertex array
    //     vertices.resize(vertexCount);
    //     for (size_t i = 0; i < vertexCount; ++i)
    //     {
    //         if (positions)
    //         {
    //             vertices[i].position = XMFLOAT3(positions[i * 3], positions[i * 3 + 1], positions[i * 3 + 2]);
    //         }
    //         if (normals)
    //         {
    //             vertices[i].normal = XMFLOAT3(normals[i * 3], normals[i * 3 + 1], normals[i * 3 + 2]);
    //         }
    //         if (texCoords)
    //         {
    //             vertices[i].texCoord = XMFLOAT2(texCoords[i * 2], texCoords[i * 2 + 1]);
    //         }
    //         if (tangents)
    //         {
    //             vertices[i].tangent = XMFLOAT4(tangents[i * 4], tangents[i * 4 + 1], tangents[i * 4 + 2], tangents[i * 4 + 3]);
    //         }
    //     }
    // }

    // void ExtractIndices(const tinygltf::Model& model, const tinygltf::Primitive& primitive, std::vector<uint32_t>& indices)
    // {
    //     if (primitive.indices >= 0)
    //     {
    //         const auto& accessor = model.accessors[primitive.indices];
    //         const auto& bufferView = model.bufferViews[accessor.bufferView];
    //         const auto& buffer = model.buffers[bufferView.buffer];

    //         const void* data = &buffer.data[bufferView.byteOffset + accessor.byteOffset];
    //         indices.resize(accessor.count);

    //         switch (accessor.componentType)
    //         {
    //         case TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT:
    //         {
    //             const uint16_t* src = static_cast<const uint16_t*>(data);
    //             for (size_t i = 0; i < accessor.count; ++i)
    //             {
    //                 indices[i] = src[i];
    //             }
    //             break;
    //         }
    //         case TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT:
    //         {
    //             const uint32_t* src = static_cast<const uint32_t*>(data);
    //             memcpy(indices.data(), src, accessor.count * sizeof(uint32_t));
    //             break;
    //         }
    //         }
    //     }
    // }

    // void CreateVertexBuffer(const std::vector<Vertex>& vertices, MeshData& meshData)
    // {
    //     size_t bufferSize = vertices.size() * sizeof(Vertex);

    //     // Create GPU buffer
    //     auto heapProps = CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT);
    //     auto resourceDesc = CD3DX12_RESOURCE_DESC::Buffer(bufferSize);

    //     m_device->CreateCommittedResource(
    //         &heapProps, D3D12_HEAP_FLAG_NONE, &resourceDesc, D3D12_RESOURCE_STATE_COPY_DEST, nullptr,
    //         IID_PPV_ARGS(&meshData.vertexBuffer)
    //     );

    //     // Create upload buffer
    //     heapProps = CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_UPLOAD);
    //     ComPtr<ID3D12Resource> uploadBuffer;

    //     m_device->CreateCommittedResource(
    //         &heapProps, D3D12_HEAP_FLAG_NONE, &resourceDesc, D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, IID_PPV_ARGS(&uploadBuffer)
    //     );

    //     // Copy data
    //     void* mappedData;
    //     uploadBuffer->Map(0, nullptr, &mappedData);
    //     memcpy(mappedData, vertices.data(), bufferSize);
    //     uploadBuffer->Unmap(0, nullptr);

    //     // Schedule GPU copy
    //     m_commandList->CopyResource(meshData.vertexBuffer.Get(), uploadBuffer.Get());

    //     // Transition to vertex buffer
    //     D3D12_RESOURCE_BARRIER barrier = {
    //         .Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION,
    //         .Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE,
    //         .Transition =
    //             {.pResource = meshData.vertexBuffer.Get(),
    //              .Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES,
    //              .StateBefore = D3D12_RESOURCE_STATE_COPY_DEST,
    //              .StateAfter = D3D12_RESOURCE_STATE_VERTEX_AND_CONSTANT_BUFFER},
    //     };

    //     m_commandList->ResourceBarrier(1, &barrier);

    //     // Setup vertex buffer view
    //     meshData.vertexBufferView.BufferLocation = meshData.vertexBuffer->GetGPUVirtualAddress();
    //     meshData.vertexBufferView.StrideInBytes = sizeof(Vertex);
    //     meshData.vertexBufferView.SizeInBytes = static_cast<UINT>(bufferSize);

    //     // Keep upload buffer alive
    //     m_uploadBuffers.push_back(uploadBuffer);
    // }

    // void CreateIndexBuffer(const std::vector<uint32_t>& indices, MeshData& meshData)
    // {
    //     if (indices.empty())
    //         return;

    //     size_t bufferSize = indices.size() * sizeof(uint32_t);

    //     // Create GPU buffer
    //     auto heapProps = CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT);
    //     auto resourceDesc = CD3DX12_RESOURCE_DESC::Buffer(bufferSize);

    //     m_device->CreateCommittedResource(
    //         &heapProps, D3D12_HEAP_FLAG_NONE, &resourceDesc, D3D12_RESOURCE_STATE_COPY_DEST, nullptr, IID_PPV_ARGS(&meshData.indexBuffer)
    //     );

    //     // Create upload buffer
    //     heapProps = CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_UPLOAD);
    //     ComPtr<ID3D12Resource> uploadBuffer;

    //     m_device->CreateCommittedResource(
    //         &heapProps, D3D12_HEAP_FLAG_NONE, &resourceDesc, D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, IID_PPV_ARGS(&uploadBuffer)
    //     );

    //     // Copy data
    //     void* mappedData;
    //     uploadBuffer->Map(0, nullptr, &mappedData);
    //     memcpy(mappedData, indices.data(), bufferSize);
    //     uploadBuffer->Unmap(0, nullptr);

    //     // Schedule GPU copy
    //     m_commandList->CopyResource(meshData.indexBuffer.Get(), uploadBuffer.Get());

    //     // Transition to index buffer
    //     auto barrier = CD3DX12_RESOURCE_BARRIER::Transition(
    //         meshData.indexBuffer.Get(), D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_INDEX_BUFFER
    //     );
    //     m_commandList->ResourceBarrier(1, &barrier);

    //     // Setup index buffer view
    //     meshData.indexBufferView.BufferLocation = meshData.indexBuffer->GetGPUVirtualAddress();
    //     meshData.indexBufferView.Format = DXGI_FORMAT_R32_UINT;
    //     meshData.indexBufferView.SizeInBytes = static_cast<UINT>(bufferSize);

    //     // Keep upload buffer alive
    //     m_uploadBuffers.push_back(uploadBuffer);
    // }

public:
    // Accessors for render loop
    const std::vector<MeshData>& GetMeshes() const { return m_meshes; }
    const std::vector<TextureData>& GetTextures() const { return m_textures; }

    // Call after GPU operations complete to release upload buffers
    //  void ReleaseUploadBuffers() { m_uploadBuffers.clear(); }
}; //;oid ProcessPrimitive(const tinygltf::Model& model, const tinygltf::Primitive& primitive

} // namespace SL